package com.laen.demo;

import java.io.IOException;
import java.util.stream.IntStream;

import com.laen.demo.task.DroolsTask;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.internal.io.ResourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;


/**
 * 任务测试
 * 
 * @author liuhuixi
 *
 */
public class CwyDroolsEngineTest {
    private static final Logger log = LoggerFactory.getLogger(CwyDroolsEngineTest.class);
    private static final String RULE_PATH = "templates/absent/";
    private KieSession kieSession;
    private AbsentFact personFact;
    private AbsentFact absentInfoFact;

    @Before
    public void setup() {
        KieServices service = KieServices.Factory.get();

        final KieRepository kieRepository = service.getRepository();
        kieRepository.addKieModule(() -> kieRepository.getDefaultReleaseId());
        KieBuilder kieBuilder = service.newKieBuilder(kieFileSystem(service));
        kieBuilder.buildAll();
        KieContainer kieContainer = service.newKieContainer(kieRepository.getDefaultReleaseId());
        kieSession = kieContainer.newKieSession();

        personFact = new AbsentFact();
        absentInfoFact = new AbsentFact();
    }

    private KieFileSystem kieFileSystem(KieServices service) {
        KieFileSystem kieFileSystem = service.newKieFileSystem();
        try {
            for (Resource resource : getRuleFiles()) {
                kieFileSystem.write(ResourceFactory.newClassPathResource(RULE_PATH + resource.getFilename(), "UTF=8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath:" + RULE_PATH + "**/**");
    }

    @Test
    public void test1to7DaysAbsent() {
        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(100);
        info.setCountOfContinuousAbsent(0);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(0, 7).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(7L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(93L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(DroolsTask.ScoreLevel.A,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    @Test
    public void test8to20DaysAbsent() {
        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(93);
        info.setCountOfContinuousAbsent(7);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(7, 20).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(20L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(80L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(DroolsTask.ScoreLevel.B,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    @Test
    public void test21to30DaysAbsent() {
        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(80);
        info.setCountOfContinuousAbsent(20);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(20, 30).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(30L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(60L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(DroolsTask.ScoreLevel.C,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    @Test
    public void testOver30DaysAbsent() {
        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(60);
        info.setCountOfContinuousAbsent(30);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(30, 32).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(32L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(40L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(DroolsTask.ScoreLevel.D,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    @Test
    public void testOver36DaysAbsentWithZeroScore() {

        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(100);
        info.setCountOfContinuousAbsent(0);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(0, 37).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(37L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(0L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(DroolsTask.ScoreLevel.D,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    @Test
    public void testWorkDaysOverAbsentDaysThen100Scores() {
        DroolsTask.Person p = new DroolsTask.Person(111L);
        p.setAbsent(true);
        personFact.fact = p;

        DroolsTask.Result info = new DroolsTask.Result(111L);
        info.setScore(100);
        info.setCountOfContinuousAbsent(0);
        absentInfoFact.fact = info;

        personFact.factHandle = kieSession.insert(p);
        absentInfoFact.factHandle = kieSession.insert(info);

        IntStream.range(0, 37).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(37L, (long)((DroolsTask.Result)absentInfoFact.fact).getCountOfContinuousAbsent());
        Assert.assertEquals(0L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(37F, ((DroolsTask.Person)personFact.fact).getAbsentDays(), 0.1F);
        Assert.assertEquals(DroolsTask.ScoreLevel.D,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());

        p.setAbsent(false);
        IntStream.range(0, 38).forEach((x) -> triggerKie(personFact, absentInfoFact));
        Assert.assertEquals(100L, (long)((DroolsTask.Result)absentInfoFact.fact).getScore());
        Assert.assertEquals(0F, ((DroolsTask.Person)personFact.fact).getAbsentDays(), 0.1F);
        Assert.assertEquals(DroolsTask.ScoreLevel.A,
            ((DroolsTask.Result)absentInfoFact.fact).getScoreLevel());
    }

    private void triggerKie(AbsentFact... absentFacts) {
        for (AbsentFact fact : absentFacts) {
            kieSession.update(fact.factHandle, fact.fact);
        }
        kieSession.fireAllRules();
    }

    private class AbsentFact {
        Object fact;
        FactHandle factHandle;

    }
}
